﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Net;
using Altbrot.Yawr.Entities;

namespace Altbrot.Yawr.Pls
{
    public class PLSHelper
    {
        private string pls;
        private int count;
        private string[] locations;
        private string[] titles;
        private string[] lengths;
        private int version;

        #region Properties
        public int Count
        {
            get { return count; }
        }

        public string[] Locations
        {
            get { return locations; }
        }

        public string[] Titles
        {
            get { return titles; }
        }

        public string[] Lengths
        {
            get { return lengths; }
        }

        public int Version
        {
            get { return version; }
        }
        #endregion

        public void ClearPlaylist()
        {
            locations = new string[0];
            titles = new string[0];
            lengths = new string[0];
            version = 2;
            pls = string.Empty;
            count = 0;
        }

        public void Add(string title, string location, string length)
        {
            AddEntry(title, ref titles);
            AddEntry(location, ref locations);
            AddEntry(length, ref lengths);
            count++;
        }

        private void AddEntry(string entry, ref string[] entries)
        {
            string[] temp = new string[entries.Length + 1];
            entries.CopyTo(temp, 0);
            temp[entries.Length] = entry;
            entries = temp;
        }

        public void LoadPlaylist(Uri location)
        {            
            using (StreamReader reader = new StreamReader(location.AbsolutePath))
            {
                pls = reader.ReadToEnd();
                pls = pls.ToLower(); 
            }

            CreateProperty("NumberOfEntries=", 1, 0, out count);
            CreateProperty("Version=", 1, 0, out version);

            CreatePlaylist();
        }

        public void LoadPlaylist(string plsString)
        {
            pls = plsString;
            pls = pls.ToLower();

            CreateProperty("NumberOfEntries=", 1, 0, out count);
            CreateProperty("Version=", 1, 0, out version);

            CreatePlaylist();
        }

        private void CreatePlaylist()
        {
            CreateProperties("File", out locations);
            CreateProperties("Title", out titles);
            CreateProperties("Length", out lengths);
        }

        private void CreateProperty(string name, int offset, int startIndex, out int property)
        {
            try
            {
                string[] splits = pls.Split(new string[] { name.ToLower() }, StringSplitOptions.None);
                if (splits.Length > 1)
                {
                    string split = splits[offset];
                    int endIndex = IndexOfNonNumber(split);
                    property = int.Parse(splits[offset].Substring(startIndex, endIndex));
                }
                else
                {
                    property = 0;
                }
            }
            catch
            {
                property = 0;
            }
        }

        private int IndexOfNonNumber(string buffer)
        {
            for (int i = 0; i < buffer.Length; i++)
            {
                if (!char.IsNumber(buffer[i]))
                    return i;
            }
            return buffer.Length;
        }

        private void CreateProperties(string name, out string[] property)
        {
            property = new string[count];
            string[] splits = pls.Split(new string[] { name.ToLower() }, StringSplitOptions.None);
            if (splits.Length > 1)
            {
                for (int i = 0; i < count; i++)
                {
                    string split = splits[i + 1];
                    split = split.Remove(0, string.Format("{0}=", i + 1).Length);
                    if (split.Contains("\n"))
                    {
                        split = split.Substring(0, split.IndexOf("\n"));
                    }
                    property[i] = split;
                }
            }
            else if (splits.Length == 1)
            {
                for (int i = 0; i < count; i++)
                {
                    property[i] = "Empty";
                }
            }
        }

        /// <summary>
        /// Gets the feed items form playlist.
        /// </summary>
        /// <param name="plsUrl">The PLS URL.</param>
        /// <returns></returns>
        public List<FeedItem> GetFeedItemsFormPlaylist(string plsUrl, string tempFilePath)
        {
            if (DownloadFile(plsUrl, tempFilePath))
            {
                PLSHelper helper = new PLSHelper();
                helper.LoadPlaylist(new Uri(tempFilePath));
                List<FeedItem> feedItems = new List<FeedItem>();

                for (int i = 0; i < helper.Count; i++)
                {
                    feedItems.Add(new FeedItem()
                    {
                        Title = helper.Titles[i],
                        Link = helper.Locations[i],
                        Category = FeedItemCategory.Radio.ToString()
                    });
                }

                return feedItems;
            }

            return null;
        }

        /// <summary>
        /// Gets the PLS string from playlist.
        /// </summary>
        /// <param name="plsUrl">The PLS URL.</param>
        /// <param name="tempFilePath">The temp file path.</param>
        /// <returns></returns>
        public string GetPlsStringFromPlaylist(string plsUrl, string tempFilePath)
        {
            if (DownloadFile(plsUrl, tempFilePath))
            {
                using (StreamReader reader = new StreamReader(tempFilePath))
                {
                    return reader.ReadToEnd().ToLower();
                }
            }
            return string.Empty;
        }

        /// <summary>
        /// Gets the feed items form playlist.
        /// </summary>
        /// <param name="plsUrl">The PLS URL.</param>
        /// <returns></returns>
        public List<FeedItem> GetFeedItemsFormPlaylist(string plsString)
        {
            if (!string.IsNullOrEmpty(plsString))
            {
                PLSHelper helper = new PLSHelper();
                helper.LoadPlaylist(plsString);
                List<FeedItem> feedItems = new List<FeedItem>();

                for (int i = 0; i < helper.Count; i++)
                {
                    feedItems.Add(new FeedItem()
                    {
                        Title = helper.Titles[i],
                        Link = helper.Locations[i],
                        Category = FeedItemCategory.Radio.ToString()
                    });
                }

                return feedItems;
            }

            return null;
        }

        /// <summary>
        /// Downloads the file.
        /// </summary>
        /// <param name="remoteFilename">The remote filename.</param>
        /// <param name="localFilename">The local filename.</param>
        /// <returns></returns>
        private static bool DownloadFile(String remoteFilename, String localFilename)
        {
            // Function will return the number of bytes processed
            // to the caller. Initialize to 0 here.
            int bytesProcessed = 0;

            // Assign values to these objects here so that they can
            // be referenced in the finally block
            Stream remoteStream = null;
            Stream localStream = null;
            WebResponse response = null;

            // Use a try/catch/finally block as both the WebRequest and Stream
            // classes throw exceptions upon error
            try
            {
                // Create a request for the specified remote file name
                WebRequest request = WebRequest.Create(remoteFilename);
                if (request != null)
                {
                    // Send the request to the server and retrieve the
                    // WebResponse object
                    response = request.GetResponse();
                    if (response != null)
                    {
                        // Once the WebResponse object has been retrieved,
                        // get the stream object associated with the response's data
                        remoteStream = response.GetResponseStream();

                        // Create the local file
                        localStream = File.Create(localFilename);

                        // Allocate a 1k buffer
                        byte[] buffer = new byte[1024];
                        int bytesRead;

                        // Simple do/while loop to read from stream until
                        // no bytes are returned
                        do
                        {
                            // Read data (up to 1k) from the stream
                            bytesRead = remoteStream.Read(buffer, 0, buffer.Length);

                            // Write the data to the local file
                            localStream.Write(buffer, 0, bytesRead);

                            // Increment total bytes processed
                            bytesProcessed += bytesRead;
                        } while (bytesRead > 0);
                    }
                }
            }
            catch (Exception)
            {
                return false;
            }
            finally
            {
                // Close the response and streams objects here
                // to make sure they're closed even if an exception
                // is thrown at some point
                if (response != null) response.Close();
                if (remoteStream != null) remoteStream.Close();
                if (localStream != null) localStream.Close();
            }

            return true;
        }
    }
}
